/*
 * Copyright (c) 2005, 2014 vacoor
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package org.vacoor.nothing.security;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.*;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringValueResolver;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Visitor class for traversing {@link org.springframework.beans.factory.config.BeanDefinition} objects, in particular
 * the property values and constructor argument values contained in them,
 * resolving bean metadata values.
 *
 * <p>Used by {@link org.springframework.beans.factory.config.PropertyPlaceholderConfigurer} to parse all String values
 * contained in a BeanDefinition, resolving any placeholders found.
 *
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 1.2
 * @see org.springframework.beans.factory.config.BeanDefinition
 * @see org.springframework.beans.factory.config.BeanDefinition#getPropertyValues
 * @see org.springframework.beans.factory.config.BeanDefinition#getConstructorArgumentValues
 * @see org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
 */
public class BeanDefinitionVisitor2 {

	private StringValueResolver valueResolver;


	/**
	 * Create a new BeanDefinitionVisitor, applying the specified
	 * value resolver to all bean metadata values.
	 * @param valueResolver the StringValueResolver to apply
	 */
	public BeanDefinitionVisitor2(StringValueResolver valueResolver) {
		Assert.notNull(valueResolver, "StringValueResolver must not be null");
		this.valueResolver = valueResolver;
	}

	/**
	 * Create a new BeanDefinitionVisitor for subclassing.
	 * Subclasses need to override the {@link #resolveStringValue} method.
	 */
	protected BeanDefinitionVisitor2() {
	}


	public void visitBeanDefinition(String propertyPath, BeanDefinition beanDefinition) {
		visitPropertyValues(propertyPath, beanDefinition.getPropertyValues());
	}

	protected void visitPropertyValues(String propertyPath, MutablePropertyValues pvs) {
		PropertyValue[] pvArray = pvs.getPropertyValues();
		for (PropertyValue pv : pvArray) {
			Object newVal = resolveValue(propertyPath + "." + pv.getName(), pv.getValue());
			if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
				pvs.add(pv.getName(), newVal);
			}
		}
	}


	@SuppressWarnings("rawtypes")
	protected Object resolveValue(String propertyPath, Object value) {
		if (value instanceof BeanDefinition) {
			visitBeanDefinition(propertyPath, (BeanDefinition) value);
		}
		else if (value instanceof BeanDefinitionHolder) {
			visitBeanDefinition(propertyPath, ((BeanDefinitionHolder) value).getBeanDefinition());
		}
		else if (value instanceof RuntimeBeanReference) {
			RuntimeBeanReference ref = (RuntimeBeanReference) value;
			String newBeanName = resolveStringValue(propertyPath, ref.getBeanName());
			if (!newBeanName.equals(ref.getBeanName())) {
				return new RuntimeBeanReference(newBeanName);
			}
		}
		else if (value instanceof RuntimeBeanNameReference) {
			RuntimeBeanNameReference ref = (RuntimeBeanNameReference) value;
			String newBeanName = resolveStringValue(propertyPath
                    , ref.getBeanName());
			if (!newBeanName.equals(ref.getBeanName())) {
				return new RuntimeBeanNameReference(newBeanName);
			}
		}
		else if (value instanceof Object[]) {
			visitArray(propertyPath, (Object[]) value);
		}
		else if (value instanceof List) {
			visitList(propertyPath, (List) value);
		}
		else if (value instanceof Set) {
			visitSet(propertyPath, (Set) value);
		}
		else if (value instanceof Map) {
			visitMap(propertyPath, (Map) value);
		}
		else if (value instanceof TypedStringValue) {
			TypedStringValue typedStringValue = (TypedStringValue) value;
			String stringValue = typedStringValue.getValue();
			if (stringValue != null) {
				String visitedString = resolveStringValue(propertyPath, stringValue);
				typedStringValue.setValue(visitedString);
			}
		}
		else if (value instanceof String) {
			return resolveStringValue(propertyPath, (String) value);
		}
		return value;
	}

	protected void visitArray(String propertyPath, Object[] arrayVal) {
        /*
		for (int i = 0; i < arrayVal.length; i++) {
			Object elem = arrayVal[i];
			Object newVal = resolveValue(propertyPath + "[]", elem);
			if (!ObjectUtils.nullSafeEquals(newVal, elem)) {
				arrayVal[i] = newVal;
			}
		}
		*/
	}

	@SuppressWarnings({"unchecked", "rawtypes"})
	protected void visitList(String propertyPath, List listVal) {
        /*
		for (int i = 0; i < listVal.size(); i++) {
			Object elem = listVal.get(i);
			Object newVal = resolveValue(propertyPath + "[]", elem);
			if (!ObjectUtils.nullSafeEquals(newVal, elem)) {
				listVal.set(i, newVal);
			}
		}
		*/
	}

	@SuppressWarnings({"unchecked", "rawtypes"})
	protected void visitSet(String propertyPath, Set setVal) {
        /*
		Set newContent = new LinkedHashSet();
		boolean entriesModified = false;
		for (Object elem : setVal) {
			int elemHash = (elem != null ? elem.hashCode() : 0);
			Object newVal = resolveValue(propertyPath + "[]", elem);
			int newValHash = (newVal != null ? newVal.hashCode() : 0);
			newContent.add(newVal);
			entriesModified = entriesModified || (newVal != elem || newValHash != elemHash);
		}
		if (entriesModified) {
			setVal.clear();
			setVal.addAll(newContent);
		}
		*/
	}

	@SuppressWarnings({"unchecked", "rawtypes"})
	protected void visitMap(String propertyPath, Map<?, ?> mapVal) {
        /*
		Map newContent = new LinkedHashMap();
		boolean entriesModified = false;
		for (Map.Entry entry : mapVal.entrySet()) {
			Object key = entry.getKey();
			int keyHash = (key != null ? key.hashCode() : 0);
			Object newKey = resolveValue(key);
			int newKeyHash = (newKey != null ? newKey.hashCode() : 0);
			Object val = entry.getValue();
			Object newVal = resolveValue(val);
			newContent.put(newKey, newVal);
			entriesModified = entriesModified || (newVal != val || newKey != key || newKeyHash != keyHash);
		}
		if (entriesModified) {
			mapVal.clear();
			mapVal.putAll(newContent);
		}
		*/
	}

	/**
	 * Resolve the given String value, for example parsing placeholders.
	 * @param strVal the original String value
	 * @return the resolved String value
	 */
	protected String resolveStringValue(String propertyPath, String strVal) {
		String resolvedValue = doResolveStringValue(propertyPath, strVal);
		// Return original String if not modified.
		return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
	}

    protected String doResolveStringValue(String propertyPath, String strVal) {
        return strVal;
    }

}

